Skip to content

ref(scm): Implement sentry-scm dependency#112969

Draft
cmanallen wants to merge 8 commits intomasterfrom
cmanallen/implement-scm-platform
Draft

ref(scm): Implement sentry-scm dependency#112969
cmanallen wants to merge 8 commits intomasterfrom
cmanallen/implement-scm-platform

Conversation

@cmanallen
Copy link
Copy Markdown
Member

No description provided.

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Apr 14, 2026
@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Apr 14, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🚨 Warning: This pull request contains Frontend and Backend changes!

It's discouraged to make changes to Sentry's Frontend and Backend in a single pull request. The Frontend and Backend are not atomically deployed. If the changes are interdependent of each other, they must be separated into two pull requests and be made forward or backwards compatible, such that the Backend or Frontend can be safely deployed independently.

Have questions? Please ask in the #discuss-dev-infra channel.

@sentry
Copy link
Copy Markdown
Contributor

sentry bot commented Apr 14, 2026

Sentry Snapshot Testing

Name Added Removed Modified Renamed Unchanged Status
sentry-frontend
sentry-frontend
0 0 0 0 204 ✅ Unchanged

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 14, 2026

Backend Test Failures

Failures on b0c0f35 in this run:

tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_end_to_endlog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
.venv/lib/python3.13/site-packages/scm/rpc/client.py:79: in fetch_repository
    resp = msgspec.json.decode(response.content, type=ErrorResponse)
E   msgspec.ValidationError: Object missing required field `errors`

The above exception was the direct cause of the following exception:
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:81: in fetch_repository
    raise SCMCodedError(code="rpc_errors_could_not_be_deserialized") from e
E   scm.errors.SCMCodedError: ('rpc_errors_could_not_be_deserialized', 'The error response could not be deserialized.')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_get_missing_authlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
.venv/lib/python3.13/site-packages/scm/rpc/client.py:79: in fetch_repository
    resp = msgspec.json.decode(response.content, type=ErrorResponse)
E   msgspec.ValidationError: Object missing required field `errors`

The above exception was the direct cause of the following exception:
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:81: in fetch_repository
    raise SCMCodedError(code="rpc_errors_could_not_be_deserialized") from e
E   scm.errors.SCMCodedError: ('rpc_errors_could_not_be_deserialized', 'The error response could not be deserialized.')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_getlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
.venv/lib/python3.13/site-packages/scm/rpc/client.py:79: in fetch_repository
    resp = msgspec.json.decode(response.content, type=ErrorResponse)
E   msgspec.ValidationError: Object missing required field `errors`

The above exception was the direct cause of the following exception:
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:81: in fetch_repository
    raise SCMCodedError(code="rpc_errors_could_not_be_deserialized") from e
E   scm.errors.SCMCodedError: ('rpc_errors_could_not_be_deserialized', 'The error response could not be deserialized.')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_get_invalid_headerslog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
.venv/lib/python3.13/site-packages/scm/rpc/client.py:79: in fetch_repository
    resp = msgspec.json.decode(response.content, type=ErrorResponse)
E   msgspec.ValidationError: Object missing required field `errors`

The above exception was the direct cause of the following exception:
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:81: in fetch_repository
    raise SCMCodedError(code="rpc_errors_could_not_be_deserialized") from e
E   scm.errors.SCMCodedError: ('rpc_errors_could_not_be_deserialized', 'The error response could not be deserialized.')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_post_invalid_secretlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
.venv/lib/python3.13/site-packages/scm/rpc/client.py:79: in fetch_repository
    resp = msgspec.json.decode(response.content, type=ErrorResponse)
E   msgspec.ValidationError: Object missing required field `errors`

The above exception was the direct cause of the following exception:
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:81: in fetch_repository
    raise SCMCodedError(code="rpc_errors_could_not_be_deserialized") from e
E   scm.errors.SCMCodedError: ('rpc_errors_could_not_be_deserialized', 'The error response could not be deserialized.')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_post_invalid_headerslog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
.venv/lib/python3.13/site-packages/scm/rpc/client.py:79: in fetch_repository
    resp = msgspec.json.decode(response.content, type=ErrorResponse)
E   msgspec.ValidationError: Object missing required field `errors`

The above exception was the direct cause of the following exception:
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:81: in fetch_repository
    raise SCMCodedError(code="rpc_errors_could_not_be_deserialized") from e
E   scm.errors.SCMCodedError: ('rpc_errors_could_not_be_deserialized', 'The error response could not be deserialized.')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_get_invalid_secretlog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
.venv/lib/python3.13/site-packages/scm/rpc/client.py:79: in fetch_repository
    resp = msgspec.json.decode(response.content, type=ErrorResponse)
E   msgspec.ValidationError: Object missing required field `errors`

The above exception was the direct cause of the following exception:
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:81: in fetch_repository
    raise SCMCodedError(code="rpc_errors_could_not_be_deserialized") from e
E   scm.errors.SCMCodedError: ('rpc_errors_could_not_be_deserialized', 'The error response could not be deserialized.')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_post_missing_authlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
.venv/lib/python3.13/site-packages/scm/rpc/client.py:79: in fetch_repository
    resp = msgspec.json.decode(response.content, type=ErrorResponse)
E   msgspec.ValidationError: Object missing required field `errors`

The above exception was the direct cause of the following exception:
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:81: in fetch_repository
    raise SCMCodedError(code="rpc_errors_could_not_be_deserialized") from e
E   scm.errors.SCMCodedError: ('rpc_errors_could_not_be_deserialized', 'The error response could not be deserialized.')


return "wrong secret"
server = RpcServer(
secrets=settings.SCM_RPC_SHARED_SECRET or [],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential authentication bypass when SCM_RPC_SHARED_SECRET is not configured

The endpoint passes settings.SCM_RPC_SHARED_SECRET or [] to the external RpcServer, which could result in an empty secrets list if the setting is not configured. The previous implementation explicitly raised RpcAuthenticationSetupException when secrets were missing, ensuring requests would fail. The behavior of RpcServer with an empty secrets list cannot be verified (external code) - it may either reject all requests (safe) or skip authentication (vulnerable). This is a behavioral change from explicit failure to unknown behavior.

Verification

Verified by reading the diff showing the old code raised RpcAuthenticationSetupException when SCM_RPC_SHARED_SECRET was not set (lines 32-35 and 82-85 in the old code). The new code at line 18 uses settings.SCM_RPC_SHARED_SECRET or [] which defaults to an empty list. Tests in test_scm_rpc.py always use @override_settings(SCM_RPC_SHARED_SECRET=[...]) so the empty secrets scenario is not tested. The RpcServer class is from external package scm.rpc.server and its behavior with empty secrets cannot be verified.

Identified by Warden sentry-security · HE7-XD7

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 14, 2026

Backend Test Failures

Failures on 1e2ede9 in this run:

tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_get_invalid_secretlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:86: in fetch_repository
    raise exceptions[0]
E   scm.errors.SCMCodedError: ('rpc_invalid_grant', 'Invalid grant')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_get_missing_authlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:86: in fetch_repository
    raise exceptions[0]
E   scm.errors.SCMCodedError: ('rpc_invalid_grant', 'Invalid grant')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_get_invalid_headerslog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:86: in fetch_repository
    raise exceptions[0]
E   scm.errors.SCMCodedError: ('rpc_invalid_grant', 'Invalid grant')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_post_invalid_headerslog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:86: in fetch_repository
    raise exceptions[0]
E   scm.errors.SCMCodedError: ('rpc_invalid_grant', 'Invalid grant')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_post_invalid_secretlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:86: in fetch_repository
    raise exceptions[0]
E   scm.errors.SCMCodedError: ('rpc_invalid_grant', 'Invalid grant')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_end_to_endlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:86: in fetch_repository
    raise exceptions[0]
E   scm.errors.SCMCodedError: ('rpc_invalid_grant', 'Invalid grant')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_getlog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:86: in fetch_repository
    raise exceptions[0]
E   scm.errors.SCMCodedError: ('rpc_invalid_grant', 'Invalid grant')
tests/sentry/scm/endpoints/test_scm_rpc.py::TestScmRpc::test_post_missing_authlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/scm/endpoints/test_scm_rpc.py:82: in setUp
    self.rpc_client = SourceCodeManager.make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/rpc/client.py:137: in make_from_repository_id
    return super().make_from_repository_id(
.venv/lib/python3.13/site-packages/scm/manager.py:20: in make_from_repository_id
    provider = initialize_provider(
.venv/lib/python3.13/site-packages/scm/helpers.py:14: in initialize_provider
    repository = fetch_repository(organization_id, repository_id)
.venv/lib/python3.13/site-packages/scm/rpc/client.py:141: in <lambda>
    fetch_repository=lambda oid, rid: fetch_repository(full_url, signing_secret, oid, rid, session),
.venv/lib/python3.13/site-packages/scm/rpc/client.py:86: in fetch_repository
    raise exceptions[0]
E   scm.errors.SCMCodedError: ('rpc_invalid_grant', 'Invalid grant')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant